package unitTest.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class MainApplication implements EntryPoint {

	final Button formButton = new Button();
	
	final Grid resultsGrid = new Grid();
	
	final Label statusLabel = new Label();
	
	RequestBuilder builder = new RequestBuilder(
			RequestBuilder.POST, GWT.getModuleBaseURL() + "runTest");
	public void onModuleLoad() {
		final Grid inputGrid = new Grid(3, 2);
		inputGrid.setBorderWidth(1);
		inputGrid.setCellSpacing(1);
		// input 1: test cases number
		final Label testCaseNumLabel = new Label("TestCase Num:");
		testCaseNumLabel.setWidth("100px");

		final TextBox testCaseNumText = new TextBox();
		testCaseNumText.setText("5");
		testCaseNumText.setWidth("50px");

		inputGrid.setWidget(0, 0, testCaseNumLabel);
		inputGrid.setWidget(0, 1, testCaseNumText);

		// input 2:test suite
		final Label testSuiteLabel = new Label();
		testSuiteLabel.setText("TestSuite:");

		final ListBox suiteNameListBox = new ListBox(true);
		suiteNameListBox.addItem("TestSuite1");
		suiteNameListBox.addItem("TestSuite2");
		suiteNameListBox.addItem("TestSuite3");
		suiteNameListBox.addItem("TestSuite4");
		suiteNameListBox.addItem("TestSuite5");
		suiteNameListBox.setVisibleItemCount(5);

		inputGrid.setWidget(1, 0, testSuiteLabel);
		inputGrid.setWidget(1, 1, suiteNameListBox);

		// add to inputSlot
		RootPanel.get("inputSlot").add(inputGrid);

		// run test button
		formButton.setText("Run Test");
		formButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				// disable run test button
				formButton.setText("Waiting for complete ...");
				formButton.setEnabled(false);
				
				// collect user inputs
				JSONObject inputs = new JSONObject();
				int caseNum = Integer.parseInt(testCaseNumText.getText());
				inputs.put("caseNum", new JSONString(testCaseNumText.getText()));

				JSONArray suitesArray = new JSONArray();
				int j = 0;
				for (int i = 0; i < suiteNameListBox.getItemCount(); i++) {
					if (suiteNameListBox.isItemSelected(i)) {
						suitesArray.set(j, new JSONString(suiteNameListBox.getValue(i)));
						j++;
					}
				}
				
				inputs.put("testSuites", suitesArray);
				int suiteNum = suitesArray.size();

				// send request
				Request startRequest = null;
				try {
					startRequest = builder.sendRequest(inputs.toString(),new StartUnitTestRequestCallback());
				} catch (RequestException e) {
					if(startRequest!=null) {
						startRequest.cancel();
					}
					Window.alert("A request error has occurred!\n Exception:"+ e.getMessage());
				}
				// get results in interval
				Timer t = new UIRefreshTimer(caseNum, suiteNum);
				t.scheduleRepeating(3000);
				
			}
		});

		RootPanel.get("runSlot").add(formButton);

		// status slot
		statusLabel.setStyleName("status");
		statusLabel.setText("Wait for running the TestCases");

		RootPanel.get("statusSlot").add(statusLabel);

		// output slot
		resultsGrid.setBorderWidth(5);
		resultsGrid.setCellSpacing(5);
		resultsGrid.setCellPadding(10);

		RootPanel.get("outputSlot").add(resultsGrid);

	}

	/**
	 * Timer to retrieve runtime test results
	 */
	private class UIRefreshTimer extends Timer {
		private int caseNum = 0;
		private int suiteNum = 0;
		private RequestBuilder builder = null;

		public UIRefreshTimer(int caseNum, int suiteNum) {
			this.caseNum = caseNum;
			this.suiteNum = suiteNum;
			builder = new RequestBuilder(RequestBuilder.POST, GWT.getModuleBaseURL()+ "runTest");
		}

		public void run() {
			Request refreshRequest = null;
			try {
				refreshRequest = builder.sendRequest("getRuntimeResults",
						new RetrieveRuntimeResultsRequestCallback(caseNum,
								suiteNum, this));
			} catch (RequestException e) {
				if (refreshRequest != null) {
					refreshRequest.cancel();
				}
				Window.alert("Request failed - " + e.getMessage());
			}
		}
	}

	/**
	 * callback to handle response message for starting unit request
	 */
	private class StartUnitTestRequestCallback implements RequestCallback {

		public void onError(Request request, Throwable exception) {
			Window.alert("Request failed - " + exception.getMessage());
		}

		public void onResponseReceived(Request request, Response response) {
			statusLabel.setText("Displaying runtime test results ...");
		}
	}

	/**
	 * callback to handle response message for retrieving runtime results request
	 */
	private class RetrieveRuntimeResultsRequestCallback implements
			RequestCallback {
		private int caseNum = 0;
		private int suiteNum = 0;
		private Timer timer = null;

		public RetrieveRuntimeResultsRequestCallback(int caseNum, int suiteNum,
				Timer timer) {
			this.caseNum = caseNum;
			this.suiteNum = suiteNum;
			this.timer = timer;
		}

		public void onError(Request request, Throwable exception) {
			Window.alert("Get runtime results failed - "
					+ exception.getMessage());
		}

		public void onResponseReceived(Request request, Response response) {
			if (response.getStatusCode() == 200) {
				String respText = response.getText();

				// parse JSON encoded message
//				parseStrict(String) (for inputs that strictly follow the JSON specification) or parseLenient(String)
				JSONValue respValue = JSONParser.parseLenient(respText);
				JSONArray trArray = respValue.isArray();
				if (trArray != null) {
					// refresh components
					if (trArray.size() == caseNum * suiteNum) {
						timer.cancel();
						formButton.setEnabled(true);
						statusLabel.setText("Test complete !");
						formButton.setText("Run Test");
					}
					// refresh test results ui
					resultsGrid.resize(trArray.size(), 4);
//					Window.alert(new Integer(trArray.size()).toString());
					for (int i = 0; i < trArray.size(); i++) {
						JSONObject tr = trArray.get(i).isObject();
						if (tr != null) {
//							Window.alert(tr.get("name").isString().stringValue());
							resultsGrid.setWidget(i, 0, new Label(tr.get("name").isString().stringValue()));
							resultsGrid.setWidget(i, 1, new Label(tr.get("start").isString().stringValue()));
							resultsGrid.setWidget(i, 2, new Label(tr.get("end").isString().stringValue()));
							resultsGrid.setWidget(i, 3, new Label(tr.get("result").isString().stringValue()));
							resultsGrid.getCellFormatter().setStyleName(i,3,tr.get("result").isString().stringValue().equals("PASS") ? "green" : "red");
						}
						else
							Window.alert("tr null:" + i);
					}
				}
			} else {
				Window.alert("Error occurred - statusCode:"
						+ response.getStatusCode());
			}
		}
	}
	
}
